import { EventsRes, MathTool, MouseInfo, SceneGraph } from '@amcax/base';
import { ctx2 } from '@app-cad/module';
import { calculatePoint, getAngle, RETAIN } from '@app-cad/common/utils';
import { wsApi } from '@app-cad/server';
import { EllipseCurve, Geometry, Vector3 } from 'three-legacy';
import {
  SKETCH_CENTEREVENTS,
  sketchEventCenter,
} from '../../../events/SketchEventCenter';
import CmdBase from '../../CmdBase';
import { Dimension } from '../dimension/Dimension';
import { MouseState, State } from './types';

const dimension1 = new Dimension();
const dimension2 = new Dimension();
export default class CmdInteractor extends CmdBase {
  angle: number = 0; // 椭圆旋转角度
  center: Vector3; // 圆心
  startArcPoint: Vector3 = new Vector3(); //arc起点
  // EllipseCurve的角度为椭圆缩放为正圆时的角度，此处单独存一个点
  startArcPointForAngle: Vector3 = new Vector3();
  endArcPoint: Vector3 = new Vector3(); //arc终点
  endArcPointForAngle: Vector3 = new Vector3();
  xRadius: number = 0;
  yRadius: number = 0;
  vectorMajor: Vector3 = new Vector3(0, 1, 0); // 主轴方向
  currentStatus: MouseState = State.NONE;

  private _planeNormal = new Vector3(0, 0, 1);
  private _vectorMinor = new Vector3();

  get vectorMinor() {
    return this._vectorMinor
      .crossVectors(this.vectorMajor, this._planeNormal)
      .normalize();
  }

  constructor() {
    super();
    this.registerEvents();
  }

  public reset() {
    this.currentStatus = State.NONE;
    this.angle = 0;
    this.center = new Vector3();
    this.startArcPoint = new Vector3();
    this.endArcPoint = new Vector3();
    this.xRadius = 0;
    this.yRadius = 0;
    this.vectorMajor = new Vector3(0, 1, 0);
    this.removeTempPoint();
  }

  private _scaleToolVector = new Vector3();
  private scaleVector(vector: Vector3, direction: Vector3, scale: number) {
    const extraVector = direction
      .clone()
      .setLength(vector.dot(direction) * (scale - 1));

    return this._scaleToolVector.addVectors(extraVector, vector).clone();
  }

  // 椭圆上的点
  getEllipseIntersection(center: Vector3, mousePoint: MouseInfo) {
    const scale = this.xRadius / this.yRadius;
    // 计算从中心到鼠标点的向量
    const baseVector = new Vector3(
      mousePoint.x - center.x,
      mousePoint.y - center.y,
      0,
    );

    const scaledVector = this.scaleVector(
      baseVector,
      this.vectorMajor,
      1 / scale,
    ).setLength(this.yRadius);

    const finalVector = this.scaleVector(scaledVector, this.vectorMajor, scale);

    return {
      pointForAngle: center.clone().add(scaledVector),
      finalPoint: center.clone().add(finalVector),
    };
  }

  private startDrawY() {
    this.currentStatus = State.DRAW_ELLIPSE_Y;
    if (dimension1.dimType === 'preview') {
      dimension1.destroy();
    }
  }

  private startDrawArc() {
    this.currentStatus = State.DRAW_ELLIPSE_ARC;
    const { finalPoint, pointForAngle } = this.getEllipseIntersection(
      this.center,
      this.mouseWV.clone(),
    );

    this.startArcPoint = finalPoint;
    this.startArcPointForAngle = pointForAngle;
  }

  enterKeyDown = () => {
    if (this.currentStatus === State.DRAW_ELLIPSE_X) {
      const flag = dimension1.determineDim();
      if (flag) {
        this.setXRadius(dimension1.getDimInfo().value);
        this.preview();
        this.startDrawY();
      }
    } else if (this.currentStatus === State.DRAW_ELLIPSE_Y) {
      const flag = dimension2.determineDim();
      if (flag) {
        this.setYRadius(dimension2.getDimInfo().value);
        this.preview();
        this.startDrawArc();
      }
    } else if (this.currentStatus === State.DRAW_ELLIPSE_ARC) {
      this.finish();
    }
  };

  private setXRadius(value: number) {
    this.xRadius = value / 2;
    this.yRadius = MathTool.fixNumber(this.xRadius * 0.45, RETAIN);
  }

  private setYRadius(value: number) {
    this.yRadius = value / 2;
  }

  registerEvents = () => {
    sketchEventCenter()
      .setState('SketchEllipseArc')
      .on(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .on(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .on(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp)
      .addExit(this.destroy.bind(this));
  };
  unregisterEvents = () => {
    sketchEventCenter()
      .off(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .off(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .off(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp)
      .setDefault();
    dimension1?.reset();
    dimension2?.reset();
  };
  onMouseDown = (e: EventsRes) => {
    if (e.button.onLeft) {
      if (this.currentStatus === State.NONE) {
        this.center = this.mouseWV;
        this.createAssistPoint(this.center, null, null, true);
        this.currentStatus = State.DRAW_ELLIPSE_X;
      } else if (this.currentStatus === State.DRAW_ELLIPSE_X) {
        this.startDrawY();
      } else if (this.currentStatus === State.DRAW_ELLIPSE_Y) {
        this.startDrawArc();
      } else if (this.currentStatus === State.DRAW_ELLIPSE_ARC) {
        // 鼠标右键，完成
        this.finish();
      }
    } else if (e.button.onRight && !this.closeRightClick) {
      // 鼠标右键，取消绘制
      this.reset();
      this.releaseObjects();
    }
  };
  finish = async () => {
    // 鼠标右键，完成
    await this.finishDraw();
    this.reset();
    this.releaseObjects();
  };
  onMouseMove = (e: EventsRes) => {
    this.setMouseWV(e);
    if (this.currentStatus === State.DRAW_ELLIPSE_X) {
      const xRadius = this.calcRadius(this.center, this.mouseWV) * 2;
      this.vectorMajor = new Vector3()
        .subVectors(this.mouseWV, this.center)
        .normalize();

      this.setXRadius(xRadius);
      this.yRadius = MathTool.fixNumber(this.xRadius * 0.45, RETAIN);
      this.preview();
    } else if (this.currentStatus === State.DRAW_ELLIPSE_Y) {
      const yRadius =
        Math.abs(
          new Vector3()
            .subVectors(this.center, this.mouseWV)
            .dot(this.vectorMinor),
        ) * 2;
      this.setYRadius(yRadius);
      this.preview();
    } else if (this.currentStatus === State.DRAW_ELLIPSE_ARC) {
      this.preview();
    }
  };

  releaseObjects = () => {
    dimension1?.destroy();
    dimension2?.destroy();
    this.clearLine();
    this.assisGroup.clear();
    SceneGraph.emptyGroup(this.assisGroup);
    ctx2.viewer.requestRender();
  };

  isLoading = false;

  finishDraw = async () => {
    if (this.isLoading) {
      return;
    }

    const param = this.getParams();
    if (this.xRadius != 0) {
      try {
        this.isLoading = true;
        const res = await wsApi.createSketchEllipseArc(this.sketchId, param);
        await this.render({ added: res.added });
      } finally {
        this.isLoading = false;
      }
    }
  };

  preview() {
    if (this.currentStatus === State.DRAW_ELLIPSE_X) {
      const angle = this.vectorMajor.angleTo(new Vector3(1, 0, 0));
      this.angle = this.vectorMajor.y > 0 ? angle : Math.PI * 2 - angle;
      this.createUpdateLine(this.getVertices(), 'aline');
    } else if (this.currentStatus === State.DRAW_ELLIPSE_Y) {
      this.clearLine(['aline']);
      this.createUpdateLine(this.getVertices());
    } else if (this.currentStatus === State.DRAW_ELLIPSE_ARC) {
      const { finalPoint, pointForAngle } = this.getEllipseIntersection(
        this.center,
        this.mouseWV.clone(),
      );
      this.endArcPoint = finalPoint;
      this.endArcPointForAngle = pointForAngle;

      this.createUpdateLine(
        this.getVertices(
          getAngle(this.startArcPointForAngle, this.center) - this.angle, // startAngle, endAngle
          getAngle(this.endArcPointForAngle, this.center) - this.angle,
        ),
      );
    }
    this.drawAssist();
    ctx2.viewer.requestRender();
  }

  getVertices(startAngle?: number, endAngle?: number) {
    const ellipseCurve = new EllipseCurve(
      this.center.x,
      this.center.y,
      this.xRadius,
      this.yRadius,
      startAngle ?? 0,
      endAngle ?? Math.PI * 2,
      false,
      this.angle,
    );
    const geometry = new Geometry().setFromPoints(ellipseCurve.getPoints(100));
    return geometry.vertices?.length < 101 ? [] : geometry.vertices;
  }

  drawAssist() {
    const p1 = calculatePoint(this.vectorMajor, this.xRadius, this.center);
    const p2 = calculatePoint(this.vectorMajor, -this.xRadius, this.center);
    const p3 = calculatePoint(this.vectorMinor, this.yRadius, this.center);
    const p4 = calculatePoint(this.vectorMinor, -this.yRadius, this.center);
    this.createUpdateLine([p1, p2], 'yline', 1);
    this.createUpdateLine([p3, p4], 'xline', 1);
    if (this.currentStatus === State.DRAW_ELLIPSE_X) {
      dimension1.dimLine([p1, p2], 1, true, 'center');
    } else if (this.currentStatus === State.DRAW_ELLIPSE_Y) {
      dimension2.dimLine([p3, p4], -1, true, 'center');
    }
  }
  getParams() {
    // 暂不会自动绘制长短轴，故额外标注和约束也无法添加
    return {
      center: this.center.toArray(),
      minor: Math.min(this.xRadius, this.yRadius),
      major: Math.max(this.xRadius, this.yRadius),
      point1: [this.startArcPoint.x, this.startArcPoint.y],
      point2: [this.endArcPoint.x, this.endArcPoint.y],
      majorDir:
        this.xRadius > this.yRadius
          ? [this.vectorMajor.x, this.vectorMajor.y]
          : [-this.vectorMajor.y, this.vectorMajor.x],
      curveStyle: this.lineType,
    };
  }
}
